require "import"
import "android.app.*"
import "android.os.*"
import "android.widget.*"
import "android.view.*"
import "android.hardware.Camera"
import "android.graphics.SurfaceTexture"
import "java.io.File"
import "java.io.FileOutputStream"
import "android.media.MediaActionSound"
import "android.os.Environment"
import "java.lang.System"
import "android.content.Intent"
import "android.net.Uri"
import "android.graphics.BitmapFactory"
import "android.graphics.Bitmap"
import "android.graphics.Matrix"
import "android.media.MediaRecorder"
import "android.content.Context"
import "android.view.SurfaceView"
import "android.speech.SpeechRecognizer"
import "android.speech.RecognizerIntent"
import "android.speech.RecognitionListener"
import "android.content.IntentFilter"
import "android.content.BroadcastReceiver"

local prefs = service.getSharedPreferences("TalkingSelfieCameraPrefs", Context.MODE_PRIVATE)
local editor = prefs.edit()

local cameraId = prefs.getInt("cameraId", 0) 
local isSoundOn = prefs.getBoolean("isSoundOn", true)
local timerValue = prefs.getInt("timerValue", 0)
local qualityLevel = prefs.getInt("qualityLevel", 1) 
local currentMode = prefs.getString("currentMode", "PHOTO")
local currentLang = prefs.getString("currentLang", "Arabic")
local descLength = prefs.getString("descLength", "normal")

local isShakeToStopOn = prefs.getBoolean("isShakeToStopOn", true)
local shakeIntensity = prefs.getFloat("shakeIntensity", 2.5)
local videoDuration = prefs.getInt("videoDuration", 0) 
local isStopOnScreenOff = prefs.getBoolean("isStopOnScreenOff", true)

local function formatDuration(secs)
    if secs == 0 then 
        if currentLang == "Arabic" then return "إيقاف (يدوي)"
        elseif currentLang == "French" then return "Désactivé (Manuel)"
        else return "Off (Manual)" end
    end
    if secs <= 60 then 
        if currentLang == "Arabic" then return secs .. " ثانية"
        elseif currentLang == "French" then return secs .. " secondes"
        else return secs .. " seconds" end
    end
    if currentLang == "Arabic" then return (secs/60) .. " دقيقة"
    elseif currentLang == "French" then return (secs/60) .. " minute(s)"
    else return (secs/60) .. " minute(s)" end
end

local function getGuidanceStrings(lang)
    if lang == "Arabic" then
        return {
            left = "حرك الهاتف إلى اليسار",
            right = "حرك الهاتف إلى اليمين",
            up = "حرك الهاتف للأعلى",
            down = "حرك الهاتف للأسفل",
            near = "أقرب الهاتف قليلاً",
            far = "أبعد الهاتف قليلاً",
            perfect = "مثالي! استمر...",
            noFace = "لم يتم اكتشاف الوجه، أحضر الهاتف أمامك"
        }
    elseif lang == "French" then
        return {
            left = "Bougez le téléphone vers la gauche",
            right = "Bougez le téléphone vers la droite",
            up = "Bougez le téléphone vers le haut",
            down = "Bougez le téléphone vers le bas",
            near = "Rapprochez un peu le téléphone",
            far = "Éloignez un peu le téléphone",
            perfect = "Parfait! Ne bougez plus...",
            noFace = "Aucun visage détecté, placez le téléphone devant vous"
        }
    else 
        return {
            left = "Move the phone to the left",
            right = "Move the phone to the right",
            up = "Move the phone up",
            down = "Move the phone down",
            near = "Bring the phone a bit closer",
            far = "Move the phone a bit further",
            perfect = "Perfect! Hold still...",
            noFace = "No face detected, hold the phone in front of you"
        }
    end
end

local guidance = getGuidanceStrings(currentLang)

uiText = {
    Arabic = {
        title = "كاميرا الصور الذاتية المتحدثة",
        status = "الكاميرا قيد التشغيل. أمسك الهاتف أمام وجهك.",
        camSwitch = "تبديل الكاميرا",
        flash = "الفلاش: تغيير الحالة",
        modeP = "الوضع: صورة",
        modeV = "الوضع: فيديو",
        settings = "الإعدادات",
        close = "إيقاف / إغلاق",
        ai = "الرؤية الذكية للصور الذاتية المتحدثة (أدوات حية)",
        msgScreenOff = "تم قفل الشاشة. تم حفظ الفيديو.",
        msgResume = "تم استئناف الكاميرا.",
        msgClose = "تم إغلاق الكاميرا.",
        msgNoKey = "يرجى تعيين مفتاح Groq API في الإعدادات.",
        msgLangSet = "تم تعيين اللغة إلى العربية",
        msgCamOn = " تم تشغيلها."
    },
    French = {
        title = "Caméra Selfie Parlante",
        status = "Caméra active. Tenez le téléphone devant votre visage.",
        camSwitch = "Changer de caméra",
        flash = "Flash: Changer d'état",
        modeP = "Mode: Photo",
        modeV = "Mode: Vidéo",
        settings = "Paramètres",
        close = "Arrêter / Fermer",
        ai = "Vision IA Intelligente (Outils en direct)",
        msgScreenOff = "Écran verrouillé. Vidéo enregistrée.",
        msgResume = "Caméra reprise.",
        msgClose = "Caméra fermée.",
        msgNoKey = "Veuillez configurer la clé API Groq dans les paramètres.",
        msgLangSet = "Langue configurée sur le Français",
        msgCamOn = " activée."
    },
    English = {
        title = "Talking Selfie Camera",
        status = "Camera is active. Hold the phone in front of your face.",
        camSwitch = "Switch Camera",
        flash = "Flash: Toggle State",
        modeP = "Mode: Photo",
        modeV = "Mode: Video",
        settings = "Settings",
        close = "Stop / Close",
        ai = "Smart AI Vision (Live Tools)",
        msgScreenOff = "Screen locked. Video saved.",
        msgResume = "Camera resumed.",
        msgClose = "Camera closed.",
        msgNoKey = "Please set the Groq API key in settings.",
        msgLangSet = "Language set to English",
        msgCamOn = " is active."
    }
}
local t = uiText[currentLang] or uiText["Arabic"]

local function saveSettings()
    editor.putInt("cameraId", cameraId)
    editor.putBoolean("isSoundOn", isSoundOn)
    editor.putInt("timerValue", timerValue)
    editor.putInt("qualityLevel", qualityLevel)
    editor.putString("currentMode", currentMode)
    editor.putString("currentLang", currentLang)
    editor.putString("descLength", descLength)
    editor.putBoolean("isShakeToStopOn", isShakeToStopOn)
    editor.putFloat("shakeIntensity", shakeIntensity)
    editor.putInt("videoDuration", videoDuration)
    editor.putBoolean("isStopOnScreenOff", isStopOnScreenOff)
    editor.apply()
end

local layout = {
    LinearLayout, orientation="vertical", padding="10dp", layout_width="fill", layout_height="fill", backgroundColor="#000000",
    { SurfaceView, id="surface_view", layout_width="fill", layout_weight="1", layout_marginBottom="5dp" },
    { TextView, id="tv_title", text=t.title, textSize="20sp", textColor="#ffffff", gravity="center", layout_marginBottom="5dp" },
    { TextView, id="tv_status", text=t.status, textSize="14sp", textColor="#dddddd", gravity="center", layout_marginBottom="10dp" },
    {
        LinearLayout, orientation="horizontal", layout_width="fill", layout_marginBottom="5dp",
        { Button, id="btn_switch", text=t.camSwitch, layout_weight="1", backgroundColor="#2196F3", textColor="#ffffff", layout_marginRight="3dp" },
        { Button, id="btn_flash", text=((isFlashOn and "ON" or "OFF")), layout_weight="1", backgroundColor="#FF9800", textColor="#ffffff", layout_marginLeft="3dp" }
    },
    { Button, id="btn_mode", text=(currentMode == "PHOTO" and t.modeP or t.modeV), layout_width="fill", backgroundColor="#4CAF50", textColor="#ffffff", layout_marginBottom="5dp" },
    { Button, id="btn_ai", text=t.ai, layout_width="fill", backgroundColor="#009688", textColor="#ffffff", layout_marginBottom="5dp" },
    { Button, id="btn_settings", text=t.settings, layout_width="fill", backgroundColor="#607D8B", textColor="#ffffff", layout_marginBottom="5dp" },
    { Button, id="btn_close", text=t.close, layout_width="fill", backgroundColor="#D32F2F", textColor="#ffffff", padding="12dp" }
}

local dlg = LuaDialog(service)
dlg.setView(loadlayout(layout))
local window = dlg.getWindow()
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
window.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)

pcall(function()
    btn_flash.setText(currentLang == "Arabic" and ("الفلاش: " .. (isFlashOn and "تشغيل" or "إيقاف")) or (currentLang == "French" and ("Flash: " .. (isFlashOn and "Allumé" or "Éteint")) or ("Flash: " .. (isFlashOn and "ON" or "OFF"))))
end)

local camera, mediaRecorder = nil, nil
local isCapturing, isRecording, isCountingDown, isFlashOn = false, false, false, false
local lastSpeakTime = 0
local camOrientation = 90
local mainHandler = Handler(Looper.getMainLooper())
local videoSessionId = 0
local postCaptureDlg = nil

local liveObjectActive = false
local liveObjectDialog = nil
local liveObjectStopRequested = false

local dialogPauseCount = 0

local function pauseGuidance() 
    dialogPauseCount = dialogPauseCount + 1 
end

local function resumeGuidance() 
    dialogPauseCount = dialogPauseCount - 1
    if dialogPauseCount < 0 then dialogPauseCount = 0 end
end

local function attachDismissListener(dialogInstance)
    dialogInstance.setOnDismissListener(luajava.createProxy("android.content.DialogInterface$OnDismissListener", { 
        onDismiss = function() resumeGuidance() end 
    }))
end

local screenOffReceiver = nil
pcall(function()
    local filter = IntentFilter(Intent.ACTION_SCREEN_OFF)
    screenOffReceiver = luajava.createProxy("android.content.BroadcastReceiver", {
        onReceive = function(context, intent)
            if intent.getAction() == Intent.ACTION_SCREEN_OFF then
                if isRecording and isStopOnScreenOff then
                    mainHandler.post(luajava.createProxy("java.lang.Runnable", {
                        run = function()
                            if isRecording then
                                pcall(function() mediaRecorder.stop(); mediaRecorder.release(); camera.lock() end)
                                isRecording = false
                                mediaRecorder = nil
                                service.speak(t.msgScreenOff)
                                pcall(function() local i = Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); i.setData(Uri.fromFile(File(videoFilePath))); service.sendBroadcast(i) end)
                                btn_close.setText(t.close)
                                btn_close.setBackgroundColor(0xFFD32F2F)
                            end
                        end
                    }))
                end
            end
        end
    })
    service.registerReceiver(screenOffReceiver, filter)
end)

dlg.setOnDismissListener(luajava.createProxy("android.content.DialogInterface$OnDismissListener", { 
    onDismiss = function() 
        if screenOffReceiver then pcall(function() service.unregisterReceiver(screenOffReceiver) end) end
    end 
}))

local function resumeCameraPreview()
    isCapturing = false
    isCountingDown = false
    lastFaceTime = System.currentTimeMillis()
    lastSpeakTime = System.currentTimeMillis() + 1000 
    if camera then
        pcall(function() 
            camera.startPreview()
            camera.startFaceDetection() 
        end)
        service.speak(t.msgResume)
    end
end

local function closeCamera()
    if isRecording and mediaRecorder then 
        pcall(function() mediaRecorder.stop(); mediaRecorder.release(); camera.lock() end)
        isRecording = false 
    end
    if camera then
        pcall(function()
            if isFlashOn then 
                local p = camera.getParameters()
                p.setFlashMode(Camera.Parameters.FLASH_MODE_OFF)
                camera.setParameters(p) 
            end
            camera.stopFaceDetection()
            camera.stopPreview()
            camera.release()
        end)
        camera = nil
        service.speak(t.msgClose)
    end
    pcall(function() dlg.dismiss() end)
end

local function shareFile(filePath, mimeType)
    pcall(function()
        local StrictMode = luajava.bindClass("android.os.StrictMode")
        local VmPolicy = luajava.bindClass("android.os.StrictMode$VmPolicy")
        StrictMode.setVmPolicy(VmPolicy.Builder().build())
        local intent = Intent(Intent.ACTION_SEND)
        intent.setType(mimeType)
        intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(File(filePath)))
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
        local chooser = Intent.createChooser(intent, currentLang == "Arabic" and "مشاركة عبر..." or (currentLang == "French" and "Partager via..." or "Share via..."))
        chooser.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        service.startActivity(chooser)
    end)
end

local function showPostCaptureDialog(filePath, mimeType)
    if postCaptureDlg then pcall(function() postCaptureDlg.dismiss() end) end 
    local currentFile = File(filePath)
    local currentFilePath = filePath
    postCaptureDlg = LuaDialog(service)
    
    local txtTitle = "تم حفظ الملف! ماذا تريد أن تفعل الآن؟"
    local txtShare = "مشاركة"
    local txtRename = "إعادة تسمية"
    local txtDelete = "حذف"
    local txtCont = "متابعة"
    local txtCloseCam = "إغلاق الكاميرا"
    
    if currentLang == "French" then
        txtTitle = "Fichier enregistré ! Que voulez-vous faire ?"
        txtShare = "Partager"
        txtRename = "Renommer"
        txtDelete = "Supprimer"
        txtCont = "Continuer"
        txtCloseCam = "Fermer la caméra"
    elseif currentLang == "English" then
        txtTitle = "File saved! What do you want to do now?"
        txtShare = "Share"
        txtRename = "Rename"
        txtDelete = "Delete"
        txtCont = "Continue"
        txtCloseCam = "Close Camera"
    end

    local l = {
        ScrollView, layout_width="fill", layout_height="fill", backgroundColor="#ffffff",
        {
            LinearLayout, orientation="vertical", padding="20dp",
            { TextView, text=txtTitle, textSize="18sp", textColor="#000000", layout_marginBottom="15dp" },
            { Button, text=txtShare, backgroundColor="#2196F3", textColor="#ffffff", layout_marginBottom="5dp", onClick=function() 
                postCaptureDlg.dismiss()
                postCaptureDlg = nil
                closeCamera()
                shareFile(currentFilePath, mimeType)
            end },
            { Button, text=txtRename, backgroundColor="#FF9800", textColor="#ffffff", layout_marginBottom="5dp", onClick=function() 
                local rd = LuaDialog(service)
                local lblPrompt = currentLang == "Arabic" and "أدخل الاسم الجديد (بدون امتداد):" or (currentLang == "French" and "Entrez le nouveau nom :" or "Enter new name:")
                local btnSave = currentLang == "Arabic" and "حفظ" or (currentLang == "French" and "Enregistrer" or "Save")
                local rl = {
                    LinearLayout, orientation="vertical", padding="20dp", backgroundColor="#ffffff",
                    { TextView, text=lblPrompt, textColor="#000000", layout_marginBottom="10dp" },
                    { EditText, id="edit_rename", text=currentFile.getName():match("(.+)%..+"), textColor="#000000", layout_width="fill", layout_marginBottom="15dp" },
                    { Button, text=btnSave, backgroundColor="#4CAF50", textColor="#ffffff", onClick=function()
                        local ext = currentFile.getName():match("^.+(%..+)$") or ""
                        local newName = edit_rename.getText().toString() .. ext
                        local newFile = File(currentFile.getParent(), newName)
                        if currentFile.renameTo(newFile) then
                            currentFile = newFile
                            currentFilePath = newFile.getAbsolutePath()
                            service.speak(currentLang == "Arabic" and "تم تغيير الاسم بنجاح" or (currentLang == "French" and "Nom changé avec succès" or "Name changed successfully"))
                        else
                            service.speak(currentLang == "Arabic" and "فشل تغيير الاسم" or (currentLang == "French" and "Échec du changement de nom" or "Failed to rename"))
                        end
                        rd.dismiss()
                    end }
                }
                rd.setView(loadlayout(rl))
                local rw = rd.getWindow()
                rw.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)
                rw.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)
                attachDismissListener(rd)
                pauseGuidance()
                rd.show()
            end },
            { Button, text=txtDelete, backgroundColor="#795548", textColor="#ffffff", layout_marginBottom="5dp", onClick=function() 
                local confirmB = AlertDialog.Builder(service)
                confirmB.setTitle(currentLang == "Arabic" and "تأكيد" or "Confirmation")
                confirmB.setMessage(currentLang == "Arabic" and "هل أنت متأكد أنك تريد حذف هذا الملف؟" or (currentLang == "French" and "Voulez-vous vraiment supprimer ce fichier ?" or "Are you sure you want to delete this file?"))
                confirmB.setPositiveButton(currentLang == "Arabic" and "نعم" or (currentLang == "French" and "Oui" or "Yes"), function()
                    if currentFile.exists() and currentFile.delete() then
                        service.speak(currentLang == "Arabic" and "تم حذف الملف" or (currentLang == "French" and "Fichier supprimé" or "File deleted"))
                        pcall(function() postCaptureDlg.dismiss() end)
                        postCaptureDlg = nil
                        resumeCameraPreview()
                    end
                end)
                confirmB.setNegativeButton(currentLang == "Arabic" and "لا" or (currentLang == "French" and "Non" or "No"), nil)
                local cDlg = confirmB.create()
                cDlg.getWindow().setType(2032)
                attachDismissListener(cDlg)
                pauseGuidance()
                cDlg.show()
            end },
            { Button, text=txtCont, backgroundColor="#4CAF50", textColor="#ffffff", layout_marginBottom="5dp", onClick=function() 
                postCaptureDlg.dismiss()
                postCaptureDlg = nil
                resumeCameraPreview()
            end },
            { Button, text=txtCloseCam, backgroundColor="#D32F2F", textColor="#ffffff", onClick=function() 
                postCaptureDlg.dismiss()
                postCaptureDlg = nil
                closeCamera()
            end }
        }
    }
    postCaptureDlg.setView(loadlayout(l))
    local w = postCaptureDlg.getWindow()
    w.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)
    attachDismissListener(postCaptureDlg)
    pauseGuidance()
    postCaptureDlg.show()
end

local function sendVisionRequest(base64Image, promptText, callback)
    local Thread = luajava.bindClass("java.lang.Thread")
    Thread(luajava.createProxy("java.lang.Runnable", { run = function()
        pcall(function()
            local urlStr = "https://api.groq.com/openai/v1/chat/completions"
            local URL = luajava.bindClass("java.net.URL")
            local conn = URL(urlStr).openConnection()
            conn.setRequestMethod("POST")
            conn.setRequestProperty("Content-Type", "application/json")
            local apiKey = prefs.getString("groq_api_key", ""):gsub("[%s\n\r]", "")
            if apiKey == "" then
                mainHandler.post(luajava.createProxy("java.lang.Runnable", { run = function() 
                    service.speak(t.msgNoKey)
                    if callback then callback("Error: No API Key") end
                end }))
                return
            end
            conn.setRequestProperty("Authorization", "Bearer " .. apiKey)
            conn.setDoOutput(true)
            local jsonBody = '{"model": "meta-llama/llama-4-scout-17b-16e-instruct", "messages": [{"role": "user", "content": [{"type": "text", "text": "'..promptText..'"}, {"type": "image_url", "image_url": {"url": "data:image/jpeg;base64,'..base64Image..'"}}]}]}'
            local os = conn.getOutputStream()
            os.write(luajava.bindClass("java.lang.String")(jsonBody).getBytes("UTF-8"))
            os.flush()
            os.close()
            local responseCode = conn.getResponseCode()
            if responseCode == 200 then
                local br = luajava.bindClass("java.io.BufferedReader")(luajava.bindClass("java.io.InputStreamReader")(conn.getInputStream()))
                local resp = ""
                local line = br.readLine()
                while line do 
                    resp = resp .. line
                    line = br.readLine() 
                end
                br.close()
                local textStart = resp:find('"content": "')
                if not textStart then textStart = resp:find('"content":"') end
                if textStart then
                    local contentStart = resp:find('"', textStart + 10) + 1
                    local contentEnd = contentStart
                    while contentEnd <= #resp do
                        local char = resp:sub(contentEnd, contentEnd)
                        if char == '"' and resp:sub(contentEnd-1, contentEnd-1) ~= '\\' then break end
                        contentEnd = contentEnd + 1
                    end
                    local aiText = resp:sub(contentStart, contentEnd - 1)
                    aiText = aiText:gsub("\\n", "\n"):gsub("\\\"", '"'):gsub("%*", "")
                    if callback then callback(aiText) end
                else
                    if callback then callback("Failed to parse AI response.") end
                end
            else
                if callback then callback("Server Error Code: " .. responseCode) end
            end
        end)
    end})).start()
end

local function showAIResultDialog(initialText, imageData)
    local resultDlg = LuaDialog(service)
    
    local txtAiTitle = currentLang == "Arabic" and "نتيجة الذكاء الاصطناعي" or (currentLang == "French" and "Résultat de l'IA" or "AI Result")
    local txtCopy = currentLang == "Arabic" and "نسخ" or (currentLang == "French" and "Copier" or "Copy")
    local txtShare = currentLang == "Arabic" and "مشاركة" or (currentLang == "French" and "Partager" or "Share")
    local txtAsk = currentLang == "Arabic" and "سؤال" or (currentLang == "French" and "Poser une question" or "Ask")
    local txtCont = currentLang == "Arabic" and "متابعة" or (currentLang == "French" and "Continuer" or "Continue")
    local txtClose = currentLang == "Arabic" and "إغلاق" or (currentLang == "French" and "Fermer" or "Close")

    local resultLayout = {
        LinearLayout, orientation="vertical", padding="20dp", backgroundColor="#ffffff", layout_width="fill",
        { TextView, text=txtAiTitle, textSize="20sp", textColor="#2563eb", layout_marginBottom="10dp" },
        { ScrollView, layout_width="fill", layout_weight="1", layout_marginBottom="15dp",
            { TextView, id="result_text", text=initialText, textSize="16sp", textColor="#333333", layout_width="fill", textIsSelectable=true } 
        },
        { LinearLayout, orientation="horizontal", layout_width="fill", layout_marginBottom="5dp",
            { Button, text=txtCopy, backgroundColor="#4CAF50", textColor="#ffffff", layout_weight="1", layout_marginRight="5dp", onClick=function()
                pcall(function() service.getSystemService(Context.CLIPBOARD_SERVICE).setText(result_text.getText()) end)
                service.speak(currentLang == "Arabic" and "تم نسخ النص" or "Text copied")
            end},
            { Button, text=txtShare, backgroundColor="#FF9800", textColor="#ffffff", layout_weight="1", layout_marginLeft="5dp", onClick=function()
                resultDlg.dismiss()
                closeCamera()
                pcall(function()
                    local shareIntent = Intent(Intent.ACTION_SEND)
                    shareIntent.setType("text/plain")
                    shareIntent.putExtra(Intent.EXTRA_TEXT, result_text.getText())
                    local chooser = Intent.createChooser(shareIntent, txtAiTitle)
                    chooser.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                    service.startActivity(chooser)
                end)
            end},
            { Button, text=txtAsk, backgroundColor="#9C27B0", textColor="#ffffff", layout_weight="1", layout_marginLeft="5dp", onClick=function()
                if not imageData then
                    service.speak(currentLang == "Arabic" and "لا توجد صورة متاحة" or "No image available")
                    return
                end
                local askDlg = LuaDialog(service)
                local txtAskPrompt = currentLang == "Arabic" and "اسأل عن هذه الصورة" or (currentLang == "French" and "Poser une question sur cette image" or "Ask about this image")
                local txtHint = currentLang == "Arabic" and "اكتب سؤالك هنا..." or (currentLang == "French" and "Écrivez votre question ici..." or "Type your question here...")
                local txtSend = currentLang == "Arabic" and "إرسال" or (currentLang == "French" and "Envoyer" or "Send")
                local txtCancel = currentLang == "Arabic" and "إلغاء" or (currentLang == "French" and "Annuler" or "Cancel")
                
                local askLayout = {
                    LinearLayout, orientation="vertical", padding="20dp", backgroundColor="#ffffff", layout_width="fill",
                    { TextView, text=txtAskPrompt, textSize="18sp", textColor="#000000", layout_marginBottom="10dp" },
                    { EditText, id="question_input", hint=txtHint, textColor="#000000", layout_width="fill", layout_marginBottom="15dp" },
                    { LinearLayout, orientation="horizontal", layout_width="fill",
                        { Button, text=txtSend, backgroundColor="#4CAF50", textColor="#ffffff", layout_weight="1", layout_marginRight="5dp", onClick=function()
                            local question = question_input.getText().toString()
                            if question == "" then
                                service.speak(currentLang == "Arabic" and "يرجى كتابة سؤال" or "Please type a question")
                                return
                            end
                            askDlg.dismiss()
                            result_text.setText(currentLang == "Arabic" and "جاري التحميل..." or "Loading...")
                            local tgtLang = currentLang == "French" and "Répondez en français." or (currentLang == "English" and "Answer in english." or "أجب باللغة العربية بوضوح.")
                            local fullPrompt = question .. " " .. tgtLang
                            sendVisionRequest(imageData, fullPrompt, function(answer)
                                mainHandler.post(luajava.createProxy("java.lang.Runnable", { run = function()
                                    result_text.setText(answer)
                                    service.speak(answer)
                                end }))
                            end)
                        end},
                        { Button, text=txtCancel, backgroundColor="#D32F2F", textColor="#ffffff", layout_weight="1", layout_marginLeft="5dp", onClick=function() askDlg.dismiss() end }
                    }
                }
                askDlg.setView(loadlayout(askLayout))
                local w = askDlg.getWindow()
                w.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)
                w.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)
                attachDismissListener(askDlg)
                pauseGuidance()
                askDlg.show()
            end }
        },
        { LinearLayout, orientation="horizontal", layout_width="fill",
            { Button, text=txtCont, backgroundColor="#2196F3", textColor="#ffffff", layout_weight="1", layout_marginRight="5dp", onClick=function()
                resultDlg.dismiss()
                resumeCameraPreview()
            end},
            { Button, text=txtClose, backgroundColor="#D32F2F", textColor="#ffffff", layout_weight="1", layout_marginLeft="5dp", onClick=function()
                resultDlg.dismiss()
                closeCamera()
            end}
        }
    }
    resultDlg.setView(loadlayout(resultLayout))
    local w = resultDlg.getWindow()
    w.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)
    resultDlg.setCancelable(false)
    attachDismissListener(resultDlg)
    pauseGuidance()
    resultDlg.show()
end

local function startLiveObjectDetection()
    if liveObjectActive then return end
    liveObjectActive = true
    liveObjectStopRequested = false
    liveObjectDialog = LuaDialog(service)
    
    local txtLiveTitle = currentLang == "Arabic" and "كاشف الأجسام الحي مفعل" or (currentLang == "French" and "Détecteur d'objets en direct activé" or "Live Object Detector Active")
    local txtLiveStatus = currentLang == "Arabic" and "جاري اكتشاف الأجسام..." or (currentLang == "French" and "Détection des objets..." or "Detecting objects...")
    local txtStop = currentLang == "Arabic" and "إيقاف" or (currentLang == "French" and "Arrêter" or "Stop")

    local dialogLayout = {
        LinearLayout, orientation="vertical", padding="20dp", backgroundColor="#ffffff", layout_width="fill",
        { TextView, text=txtLiveTitle, textSize="20sp", textColor="#2563eb", layout_marginBottom="10dp" },
        { TextView, id="live_status", text=txtLiveStatus, textSize="16sp", textColor="#333333", layout_marginBottom="15dp", layout_height="wrap", textIsSelectable=true },
        { Button, text=txtStop, backgroundColor="#D32F2F", textColor="#ffffff", onClick=function()
            liveObjectStopRequested = true
            liveObjectActive = false
            if liveObjectDialog then liveObjectDialog.dismiss() end
            resumeCameraPreview()
        end }
    }
    liveObjectDialog.setView(loadlayout(dialogLayout))
    local w = liveObjectDialog.getWindow()
    w.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)
    liveObjectDialog.setCancelable(false)
    attachDismissListener(liveObjectDialog)
    pauseGuidance()
    liveObjectDialog.show()
    
    local function captureAndDetect()
        if not liveObjectActive or liveObjectStopRequested then return end
        if isCapturing then
            task(500, captureAndDetect)
            return
        end
        isCapturing = true
        pcall(function()
            camera.setOneShotPreviewCallback(luajava.createProxy("android.hardware.Camera$PreviewCallback", {
                onPreviewFrame = function(data, cam)
                    pcall(function()
                        local params = cam.getParameters()
                        local size = params.getPreviewSize()
                        local YuvImage = luajava.bindClass("android.graphics.YuvImage")
                        local Rect = luajava.bindClass("android.graphics.Rect")
                        local ByteArrayOutputStream = luajava.bindClass("java.io.ByteArrayOutputStream")
                        local yuv = YuvImage(data, params.getPreviewFormat(), size.width, size.height, nil)
                        local baos = ByteArrayOutputStream()
                        yuv.compressToJpeg(Rect(0, 0, size.width, size.height), 70, baos)
                        local jpegData = baos.toByteArray()
                        local BitmapFactory = luajava.bindClass("android.graphics.BitmapFactory")
                        local Bitmap = luajava.bindClass("android.graphics.Bitmap")
                        local bitmap = BitmapFactory.decodeByteArray(jpegData, 0, #jpegData)
                        local width = bitmap.getWidth()
                        local height = bitmap.getHeight()
                        local maxRes = 600
                        local scale = math.min(maxRes / width, maxRes / height)
                        if scale > 1 then scale = 1 end 
                        local matrix = luajava.bindClass("android.graphics.Matrix")()
                        if cameraId == 1 then 
                            matrix.postRotate(270)
                            matrix.postScale(-scale, scale) 
                        else 
                            matrix.postRotate(90)
                            matrix.postScale(scale, scale) 
                        end
                        bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true)
                        local baos2 = ByteArrayOutputStream()
                        bitmap.compress(Bitmap.CompressFormat.JPEG, 70, baos2)
                        local imgData = baos2.toByteArray()
                        local Base64 = luajava.bindClass("android.util.Base64")
                        local base64Image = Base64.encodeToString(imgData, Base64.NO_WRAP)
                        base64Image = base64Image:gsub("\n", ""):gsub("\r", ""):gsub(" ", "")
                        
                        local promptText = "ما هي الأجسام التي تراها أمام الكاميرا؟ سرد الأجسام الرئيسية فقط. أجب بالعربية."
                        if currentLang == "French" then
                            promptText = "Quels objets voyez-vous devant la caméra ? Énumérez uniquement les objets principaux en français."
                        elseif currentLang == "English" then
                            promptText = "What objects do you see in front of the camera? List main objects only in English."
                        end

                        sendVisionRequest(base64Image, promptText, function(aiText)
                            mainHandler.post(luajava.createProxy("java.lang.Runnable", { run = function()
                                if liveObjectActive and liveObjectDialog then
                                    local statusView = liveObjectDialog.getWindow().getDecorView().findViewById(service.getResources().getIdentifier("live_status", "id", service.getPackageName()))
                                    if statusView then statusView.setText(aiText) end
                                    service.speak(aiText)
                                end
                                isCapturing = false
                                if liveObjectActive and not liveObjectStopRequested then
                                    task(5000, captureAndDetect)
                                end
                            end }))
                        end)
                    end)
                    isCapturing = false
                end
            }))
        end)
    end
    task(500, captureAndDetect)
end

local function runAIVision(promptType, customPrompt, detailLevelOverride)
    local apiKey = prefs.getString("groq_api_key", ""):gsub("[%s\n\r]", "")
    if apiKey == "" then
        service.speak(t.msgNoKey)
        return
    end
    service.speak(currentLang == "Arabic" and "جاري التقاط الصورة..." or (currentLang == "French" and "Capture de l'image..." or "Taking picture..."))
    if isCapturing then return end
    isCapturing = true
    if isSoundOn then 
        local s = MediaActionSound()
        s.load(MediaActionSound.SHUTTER_CLICK)
        task(200, function() s.play(MediaActionSound.SHUTTER_CLICK) end) 
    end
    pcall(function()
        camera.setOneShotPreviewCallback(luajava.createProxy("android.hardware.Camera$PreviewCallback", {
            onPreviewFrame = function(data, cam)
                service.speak(currentLang == "Arabic" and "الذكاء الاصطناعي يحلل..." or (currentLang == "French" and "Analyse de l'IA..." or "AI analyzing..."))
                local Thread = luajava.bindClass("java.lang.Thread")
                Thread(luajava.createProxy("java.lang.Runnable", { run = function()
                    pcall(function()
                        local params = cam.getParameters()
                        local size = params.getPreviewSize()
                        local YuvImage = luajava.bindClass("android.graphics.YuvImage")
                        local Rect = luajava.bindClass("android.graphics.Rect")
                        local ByteArrayOutputStream = luajava.bindClass("java.io.ByteArrayOutputStream")
                        local yuv = YuvImage(data, params.getPreviewFormat(), size.width, size.height, nil)
                        local baos = ByteArrayOutputStream()
                        yuv.compressToJpeg(Rect(0, 0, size.width, size.height), 80, baos)
                        local jpegData = baos.toByteArray()
                        local BitmapFactory = luajava.bindClass("android.graphics.BitmapFactory")
                        local Bitmap = luajava.bindClass("android.graphics.Bitmap")
                        local bitmap = BitmapFactory.decodeByteArray(jpegData, 0, #jpegData)
                        local width = bitmap.getWidth()
                        local height = bitmap.getHeight()
                        local maxRes = 600
                        local scale = math.min(maxRes / width, maxRes / height)
                        if scale > 1 then scale = 1 end 
                        local matrix = luajava.bindClass("android.graphics.Matrix")()
                        if cameraId == 1 then 
                            matrix.postRotate(270)
                            matrix.postScale(-scale, scale) 
                        else 
                            matrix.postRotate(90)
                            matrix.postScale(scale, scale) 
                        end
                        bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true)
                        local baos2 = ByteArrayOutputStream()
                        bitmap.compress(Bitmap.CompressFormat.JPEG, 70, baos2)
                        local imgData = baos2.toByteArray()
                        local Base64 = luajava.bindClass("android.util.Base64")
                        local base64Image = Base64.encodeToString(imgData, Base64.NO_WRAP)
                        base64Image = base64Image:gsub("\n", ""):gsub("\r", ""):gsub(" ", "")
                        local promptText = ""
                        
                        local langClause = " أجب بالعربية."
                        if currentLang == "French" then langClause = " Répondez en français."
                        elseif currentLang == "English" then langClause = " Answer in English." end

                        if promptType == "scene" then
                            local level = detailLevelOverride or descLength
                            if level == "short" then
                                if currentLang == "Arabic" then promptText = "صف هذا المشهد بجملة واحدة موجزة. أجب بالعربية."
                                elseif currentLang == "French" then promptText = "Décrivez cette scène en une seule phrase concise. Répondez en français."
                                else promptText = "Describe this scene in one concise sentence. Answer in English." end
                            elseif level == "detailed" then
                                if currentLang == "Arabic" then promptText = "صف هذا المشهد بتفصيل كامل: الأجسام، الألوان، الإضاءة والتفاصيل البارزة. أجب بالعربية."
                                elseif currentLang == "French" then promptText = "Décrivez cette scène en détail : objets, couleurs, éclairage. Répondez en français."
                                else promptText = "Describe this scene in full detail: objects, colors, lighting. Answer in English." end
                            else
                                if currentLang == "Arabic" then promptText = "صف هذا المشهد بوضوح. أجب بالعربية."
                                elseif currentLang == "French" then promptText = "Décrivez clairement cette scène. Répondez en français."
                                else promptText = "Describe this scene clearly. Answer in English." end
                            end
                        elseif promptType == "currency" then
                            if currentLang == "Arabic" then
                                promptText = "حدد الأوراق النقدية أو العملات المعدنية في هذه الصورة. تعرف بشكل خاص وعالي الدقة على الدينار الجزائري بجميع فئاته (مثل 200 دج، 500 دج، 1000 دج، 2000 دج) والعملات الورقية والمعدنية الأوروبية كاليورو. اذكر القيمة والبلد فقط بوضوح شديد. أجب بالعربية."
                            elseif currentLang == "French" then
                                promptText = "Identifiez les billets ou pièces de monnaie sur cette image. Reconnaissez spécifiquement le Dinar Algérien (DZD) et les monnaies européennes (Euro). Indiquez uniquement la valeur et le pays. Répondez en français."
                            else
                                promptText = "Identify the banknotes or coins in this image. Specifically recognize the Algerian Dinar (DZD) and European currencies (Euro). State only the value and country. Answer in English."
                            end
                        elseif promptType == "voice" then
                            promptText = customPrompt .. langClause
                        else
                            promptText = customPrompt
                        end
                        sendVisionRequest(base64Image, promptText, function(aiText)
                            mainHandler.post(luajava.createProxy("java.lang.Runnable", { run = function()
                                service.speak(aiText)
                                local imageForAsk = (promptType == "scene") and base64Image or nil
                                showAIResultDialog(aiText, imageForAsk)
                            end }))
                        end)
                    end)
                end})).start()
            end
        }))
    end)
end

local function startVoiceInput()
    local speechRec = SpeechRecognizer.createSpeechRecognizer(service)
    local speechIntent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
    speechIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM)
    
    local speechLang = "ar-SA"
    if currentLang == "French" then speechLang = "fr-FR"
    elseif currentLang == "English" then speechLang = "en-US" end
    speechIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, speechLang)
    
    local voiceDlg = LuaDialog(service)
    local txtListening = currentLang == "Arabic" and "جاري الاستماع... تكلم الآن!" or (currentLang == "French" and "Écoute en cours... parlez maintenant !" or "Listening... speak now!")
    local txtCancel = currentLang == "Arabic" and "إلغاء" or (currentLang == "French" and "Annuler" or "Cancel")

    local l = {
        LinearLayout, orientation="vertical", padding="30dp", backgroundColor="#ffffff", gravity="center",
        { TextView, text=txtListening, textSize="20sp", textColor="#2196F3", layout_marginBottom="10dp" },
        { Button, text=txtCancel, backgroundColor="#D32F2F", textColor="#ffffff", onClick=function() 
            pcall(function() speechRec.cancel(); speechRec.destroy() end)
            voiceDlg.dismiss() 
        end }
    }
    voiceDlg.setView(loadlayout(l))
    voiceDlg.setCancelable(false)
    local listener = luajava.createProxy("android.speech.RecognitionListener", {
        onReadyForSpeech = function(params) end,
        onBeginningOfSpeech = function() end,
        onRmsChanged = function(rmsdB) end,
        onBufferReceived = function(buffer) end,
        onEndOfSpeech = function() 
            mainHandler.post(luajava.createProxy("java.lang.Runnable", {run=function() voiceDlg.dismiss() end}))
        end,
        onError = function(err) 
            mainHandler.post(luajava.createProxy("java.lang.Runnable", {run=function() 
                service.speak(currentLang == "Arabic" and "لم أفهم، حاول مرة أخرى." or "Error, try again.")
                voiceDlg.dismiss()
                pcall(function() speechRec.destroy() end)
            end}))
        end,
        onResults = function(results)
            mainHandler.post(luajava.createProxy("java.lang.Runnable", {run=function() 
                local matches = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION)
                if matches and matches.size() > 0 then
                    local spokenText = matches.get(0)
                    voiceDlg.dismiss()
                    pcall(function() speechRec.destroy() end)
                    
                    local voicePrompt = ""
                    if currentLang == "Arabic" then
                        voicePrompt = "الأمر: '" .. spokenText .. "'. أجب مباشرة فقط. حدد ما إذا كان موجوداً في الصورة، وموقعه (يسار، يمين، وسط، أعلى، أسفل) والمسافة التقريبية. لا تتجاوز جملتين."
                    elseif currentLang == "French" then
                        voicePrompt = "Commande: '" .. spokenText .. "'. Indiquez si l'objet est présent sur l'image, sa position (gauche, droite, centre, haut, bas) et sa distance approximative. Maximum deux phrases."
                    else
                        voicePrompt = "Command: '" .. spokenText .. "'. Determine if it exists in the image, its location (left, right, center, top, bottom), and approximate distance. Max two sentences."
                    end
                    runAIVision("voice", voicePrompt)
                end
            end}))
        end,
        onPartialResults = function(partialResults) end,
        onEvent = function(eventType, params) end
    })
    speechRec.setRecognitionListener(listener)
    speechRec.startListening(speechIntent)
    local w = voiceDlg.getWindow()
    w.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)
    attachDismissListener(voiceDlg)
    pauseGuidance()
    voiceDlg.show()
    service.speak(currentLang == "Arabic" and "تكلم، ماذا تريد أن تبحث عنه؟" or (currentLang == "French" and "Parlez, que cherchez-vous ?" or "Speak, what are you looking for?"))
end

btn_ai.onClick = function()
    local aiOpts = {}
    if currentLang == "Arabic" then
        aiOpts = { "1. وصف المشهد", "2. التعرف على العملة (جزائرية / عالمية)", "3. أمر صوتي (تكلم للبحث)", "4. كاشف الأجسام الحي" }
    elseif currentLang == "French" then
        aiOpts = { "1. Description de scène", "2. Détecteur de monnaie", "3. Commande vocale", "4. Détecteur d'objets en direct" }
    else
        aiOpts = { "1. Scene Description", "2. Currency Detector", "3. Voice Command Search", "4. Live Object Detector" }
    end

    local aiD = AlertDialog.Builder(service)
    aiD.setTitle(t.ai)
    aiD.setItems(aiOpts, function(dlg, w)
        if w == 0 then runAIVision("scene")
        elseif w == 1 then runAIVision("currency")
        elseif w == 2 then startVoiceInput()
        elseif w == 3 then startLiveObjectDetection()
        end
    end)
    local dialog = aiD.create()
    dialog.getWindow().setType(2032)
    attachDismissListener(dialog)
    pauseGuidance()
    dialog.show()
end

local function setLanguage(lang)
    currentLang = lang
    guidance = getGuidanceStrings(lang)
    saveSettings()
    t = uiText[currentLang] or uiText["Arabic"]
    pcall(function()
        tv_title.setText(t.title)
        tv_status.setText(t.status)
        btn_switch.setText(t.camSwitch)
        btn_mode.setText(currentMode == "PHOTO" and t.modeP or t.modeV)
        btn_ai.setText(t.ai)
        btn_settings.setText(t.settings)
        btn_close.setText(t.close)
        btn_flash.setText(currentLang == "Arabic" and ("الفلاش: " .. (isFlashOn and "تشغيل" or "إيقاف")) or (currentLang == "French" and ("Flash: " .. (isFlashOn and "Allumé" or "Éteint")) or ("Flash: " .. (isFlashOn and "ON" or "OFF"))))
    end)
    service.speak(t.msgLangSet)
end

local function showLanguageDialog()
    local d = LuaDialog(service)
    local lblTitle = "اختر لغة الواجهة والذكاء الاصطناعي"
    if currentLang == "French" then lblTitle = "Choisir la langue de l'interface et de l'IA"
    elseif currentLang == "English" then lblTitle = "Choose interface and AI language" end

    local l = {
        LinearLayout, orientation="vertical", padding="20dp", backgroundColor="#ffffff",
        { TextView, text=lblTitle, textSize="20sp", textColor="#000000", layout_marginBottom="15dp" },
        { Button, text="العربية (Arabic)", layout_marginBottom="5dp", onClick=function() setLanguage("Arabic"); d.dismiss() end },
        { Button, text="Français (French)", layout_marginBottom="5dp", onClick=function() setLanguage("French"); d.dismiss() end },
        { Button, text="English", layout_marginBottom="5dp", onClick=function() setLanguage("English"); d.dismiss() end },
        { Button, text=(currentLang == "Arabic" and "إلغاء" or (currentLang == "French" and "Annuler" or "Cancel")), backgroundColor="#D32F2F", textColor="#ffffff", onClick=function() d.dismiss() end }
    }
    d.setView(loadlayout(l))
    local w = d.getWindow()
    w.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)
    attachDismissListener(d)
    pauseGuidance()
    d.show()
end

btn_settings.onClick = function()
    local d = LuaDialog(service)
    
    local txtSetTitle = currentLang == "Arabic" and "إعدادات الكاميرا والذكاء الاصطناعي" or (currentLang == "French" and "Paramètres de la caméra et de l'IA" or "Camera and AI Settings")
    local txtSetKey = currentLang == "Arabic" and "تعيين مفتاح Groq API" or (currentLang == "French" and "Configurer la clé API Groq" or "Set Groq API Key")
    local txtCloseSet = currentLang == "Arabic" and "إغلاق الإعدادات" or (currentLang == "French" and "Fermer les paramètres" or "Close Settings")
    local txtLangBtn = currentLang == "Arabic" and ("تغيير اللغة (" .. currentLang .. ")") or (currentLang == "French" and ("Changer de langue (" .. currentLang .. ")") or ("Change Language (" .. currentLang .. ")"))

    local l = {
        ScrollView, layout_width="fill", layout_height="fill", backgroundColor="#ffffff",
        {
            LinearLayout, orientation="vertical", padding="20dp",
            { TextView, text=txtSetTitle, textSize="20sp", textColor="#000000", layout_marginBottom="15dp" },
            { Button, text=txtSetKey, backgroundColor="#9C27B0", textColor="#ffffff", layout_marginBottom="15dp", onClick=function()
                local apiDlg = LuaDialog(service)
                local apiL = {
                    LinearLayout, orientation="vertical", padding="20dp", backgroundColor="#ffffff",
                    { TextView, text=txtSetKey, textSize="18sp", textColor="#000000", layout_marginBottom="10dp" },
                    { EditText, id="edit_key", text=prefs.getString("groq_api_key", ""), hint="API Key", textColor="#000000", layout_width="fill", layout_marginBottom="15dp" },
                    { 
                        LinearLayout, orientation="horizontal", layout_width="fill",
                        { Button, text=(currentLang == "Arabic" and "حفظ" or "Save"), backgroundColor="#4CAF50", textColor="#ffffff", layout_weight="1", layout_marginRight="5dp", onClick=function()
                            editor.putString("groq_api_key", edit_key.getText().toString()).apply()
                            service.speak(currentLang == "Arabic" and "تم حفظ مفتاح API بنجاح" or "API key saved successfully")
                            apiDlg.dismiss()
                        end},
                        { Button, text=(currentLang == "Arabic" and "إلغاء" or "Cancel"), backgroundColor="#D32F2F", textColor="#ffffff", layout_weight="1", layout_marginLeft="5dp", onClick=function() apiDlg.dismiss() end }
                    }
                }
                apiDlg.setView(loadlayout(apiL))
                local w = apiDlg.getWindow()
                w.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)
                w.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)
                attachDismissListener(apiDlg)
                pauseGuidance()
                apiDlg.show()
            end },
            { Button, text=(currentLang == "Arabic" and "مؤقت البدء: " or "Timer: ") .. (timerValue==0 and "OFF" or timerValue.."s"), layout_marginBottom="5dp", onClick=function(v)
                local opts = {"OFF", "3s", "5s", "10s", "15s", "20s", "25s", "30s", "35s", "40s"}
                local vals = {0, 3, 5, 10, 15, 20, 25, 30, 35, 40}
                local d1 = AlertDialog.Builder(service)
                d1.setTitle("Timer")
                d1.setItems(opts, function(dialog, which)
                    timerValue = vals[which+1]
                    saveSettings()
                    v.setText((currentLang == "Arabic" and "مؤقت البدء: " or "Timer: ") .. opts[which+1])
                    service.speak("Timer: " .. opts[which+1])
                end)
                local dl = d1.create()
                dl.getWindow().setType(2032)
                attachDismissListener(dl)
                pauseGuidance()
                dl.show()
            end },
            { Button, text=(currentLang == "Arabic" and "إيقاف الفيديو التلقائي: " or "Auto Stop Video: ") .. formatDuration(videoDuration), layout_marginBottom="5dp", onClick=function(v)
                local opts = {"OFF", "5s", "10s", "15s", "20s", "25s", "30s", "35s", "40s", "45s", "50s", "55s", "60s", "5m", "10m", "15m"}
                local vals = {0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 300, 600, 900}
                local d2 = AlertDialog.Builder(service)
                d2.setTitle("Duration")
                d2.setItems(opts, function(dialog, which)
                    videoDuration = vals[which+1]
                    saveSettings()
                    v.setText((currentLang == "Arabic" and "إيقاف الفيديو التلقائي: " or "Auto Stop Video: ") .. opts[which+1])
                    service.speak("Duration: " .. opts[which+1])
                end)
                local dl2 = d2.create()
                dl2.getWindow().setType(2032)
                attachDismissListener(dl2)
                pauseGuidance()
                dl2.show()
            end },
            { Button, text=txtLangBtn, backgroundColor="#2196F3", textColor="#ffffff", layout_marginBottom="10dp", onClick=function() showLanguageDialog() end },
            { Button, text=txtCloseSet, backgroundColor="#333333", textColor="#ffffff", onClick=function() d.dismiss() end }
        }
    }
    d.setView(loadlayout(l))
    local w = d.getWindow()
    w.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)
    attachDismissListener(d)
    pauseGuidance()
    d.show()
end

local function stopVideoRecording()
    if isRecording and mediaRecorder then
        pcall(function() mediaRecorder.stop() end)
        pcall(function() mediaRecorder.release() end)
        pcall(function() camera.lock() end)
        isRecording = false
        mediaRecorder = nil
        service.speak(currentLang == "Arabic" and "تم حفظ الفيديو." or "Video saved.")
        pcall(function() local intent = Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); intent.setData(Uri.fromFile(File(videoFilePath))); service.sendBroadcast(intent) end)
        btn_close.setText(t.close)
        btn_close.setBackgroundColor(0xFFD32F2F)
        showPostCaptureDialog(videoFilePath, "video/mp4")
    end
end

btn_close.onClick = function() 
    if isRecording then 
        stopVideoRecording() 
    else 
        closeCamera() 
    end 
end

dlg.setOnCancelListener(luajava.createProxy("android.content.DialogInterface$OnCancelListener", { onCancel = function() closeCamera() end }))

btn_switch.onClick = function()
    if isRecording then return end
    cameraId = (cameraId == 1) and 0 or 1
    saveSettings()
    local camName = (cameraId == 1) and (currentLang == "Arabic" and "الكاميرا الأمامية" or "Front Camera") or (currentLang == "Arabic" and "الكاميرا الخلفية" or "Back Camera")
    service.speak(camName .. t.msgCamOn)
    isFlashOn = false
    btn_flash.setText(currentLang == "Arabic" and ("الفلاش: " .. (isFlashOn and "تشغيل" or "إيقاف")) or (currentLang == "French" and ("Flash: " .. (isFlashOn and "Allumé" or "Éteint")) or ("Flash: " .. (isFlashOn and "ON" or "OFF"))))
    openCameraInstance()
end

btn_flash.onClick = function()
    if not camera or cameraId == 1 then 
        service.speak(currentLang == "Arabic" and "الفلاش يعمل فقط مع الكاميرا الخلفية." or "Flash works with back camera only.")
        return 
    end
    pcall(function()
        local p = camera.getParameters()
        if isFlashOn then 
            p.setFlashMode(Camera.Parameters.FLASH_MODE_OFF)
            isFlashOn = false
            service.speak(currentLang == "Arabic" and "تم إيقاف الفلاش" or "Flash OFF")
        else 
            p.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH)
            isFlashOn = true
            service.speak(currentLang == "Arabic" and "تم تشغيل الفلاش" or "Flash ON") 
        end
        btn_flash.setText(currentLang == "Arabic" and ("الفلاش: " .. (isFlashOn and "تشغيل" or "إيقاف")) or (currentLang == "French" and ("Flash: " .. (isFlashOn and "Allumé" or "Éteint")) or ("Flash: " .. (isFlashOn and "ON" or "OFF"))))
        camera.setParameters(p)
    end)
end

btn_mode.onClick = function()
    if isRecording then return end
    currentMode = (currentMode == "PHOTO") and "VIDEO" or "PHOTO"
    saveSettings()
    btn_mode.setText(currentMode == "PHOTO" and t.modeP or t.modeV)
    service.speak(currentMode == "PHOTO" and (currentLang == "Arabic" and "وضع الصور" or "Photo Mode") or (currentLang == "Arabic" and "وضع الفيديو" or "Video Mode"))
end

local function videoMonitorLoop()
    if not isRecording then return end
    local success, isAwake = pcall(function()
        local pm = service.getSystemService(Context.POWER_SERVICE)
        return pm.isInteractive()
    end)
    if success and not isAwake and isStopOnScreenOff then
        mainHandler.post(luajava.createProxy("java.lang.Runnable", {run=function() 
            if isRecording then stopVideoRecording() end 
        end}))
        return
    end
    task(1000, videoMonitorLoop)
end

local function startVideoRecording()
    if isRecording or isCapturing then return end
    isCapturing = true
    isRecording = true
    local success = pcall(function()
        camera.stopFaceDetection()
        camera.unlock()
        mediaRecorder = MediaRecorder()
        mediaRecorder.setCamera(camera)
        mediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER)
        mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA)
        local CamcorderProfile = luajava.bindClass("android.media.CamcorderProfile")
        local vQual = (qualityLevel == 3) and CamcorderProfile.QUALITY_LOW or CamcorderProfile.QUALITY_HIGH
        mediaRecorder.setProfile(CamcorderProfile.get(cameraId, vQual))
        if cameraId == 1 then 
            mediaRecorder.setOrientationHint(270) 
        else 
            mediaRecorder.setOrientationHint(90) 
        end
        local pubDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES).toString() .. "/TalkingSelfieCamera"
        local fileDir = File(pubDir)
        if not fileDir.exists() then fileDir.mkdirs() end
        videoFilePath = pubDir .. "/AutoVideo_" .. System.currentTimeMillis() .. ".mp4"
        mediaRecorder.setOutputFile(videoFilePath)
        mediaRecorder.prepare()
        mediaRecorder.start()
    end)
    if success then
        if isSoundOn then 
            local s = MediaActionSound()
            s.load(MediaActionSound.START_VIDEO_RECORDING)
            task(500, function() s.play(MediaActionSound.START_VIDEO_RECORDING) end) 
        end
        service.speak(currentLang == "Arabic" and "بدأ التسجيل! اضغط على زر الإيقاف." or "Recording started.")
        btn_close.setText(currentLang == "Arabic" and "إيقاف التسجيل" or "Stop Recording")
        btn_close.setBackgroundColor(0xFF4CAF50)
        task(1000, videoMonitorLoop)
        if videoDuration > 0 then
            videoSessionId = System.currentTimeMillis()
            local currentRecSession = videoSessionId
            task(videoDuration * 1000, function()
                if isRecording and videoSessionId == currentRecSession then
                    stopVideoRecording()
                end
            end)
        end
    else
        service.speak("Error initialization.")
        isRecording = false
        isCapturing = false
    end
end

local function savePicture(data)
    local success, savedFileName = pcall(function()
        local bitmap = BitmapFactory.decodeByteArray(data, 0, #data)
        local matrix = Matrix()
        if cameraId == 1 then 
            matrix.postRotate(270)
            matrix.postScale(-1, 1) 
        else 
            matrix.postRotate(90) 
        end
        local rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true)
        local pubDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).toString() .. "/TalkingSelfieCamera"
        local fileDir = File(pubDir)
        if not fileDir.exists() then fileDir.mkdirs() end
        local fileName = pubDir .. "/AutoFace_" .. System.currentTimeMillis() .. ".jpg"
        local fos = FileOutputStream(fileName)
        rotatedBitmap.compress(Bitmap.CompressFormat.JPEG, (qualityLevel == 1 and 100 or (qualityLevel == 2 and 70 or 30)), fos) 
        fos.close()
        bitmap.recycle()
        rotatedBitmap.recycle()
        return fileName
    end)
    if success then 
        service.speak(currentLang == "Arabic" and "مثالي! تم حفظ الصورة." or "Perfect! Image saved.")
        pcall(function() 
            local intent = Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE)
            intent.setData(Uri.fromFile(File(savedFileName)))
            service.sendBroadcast(intent) 
        end) 
        showPostCaptureDialog(savedFileName, "image/jpeg")
    else
        isCapturing = false
    end
end

local function executeCapture()
    if currentMode == "PHOTO" then
        if isCapturing then return end
        isCapturing = true
        if isSoundOn then 
            local s = MediaActionSound()
            s.load(MediaActionSound.SHUTTER_CLICK)
            task(200, function() s.play(MediaActionSound.SHUTTER_CLICK) end) 
        end
        local pictureCallback = luajava.createProxy("android.hardware.Camera$PictureCallback", { onPictureTaken = function(data, cam) savePicture(data) end })
        pcall(function() camera.takePicture(nil, nil, pictureCallback) end)
    else 
        startVideoRecording() 
    end
end

local lastFaceTime = System.currentTimeMillis()
local isCheckerRunning = false

local function startContinuousChecker()
    if isCheckerRunning then return end
    isCheckerRunning = true
    local function checkLoop()
        if not camera then 
            isCheckerRunning = false
            return 
        end
        if isCapturing or isCountingDown or dialogPauseCount > 0 then
            lastFaceTime = System.currentTimeMillis() 
            task(1000, checkLoop)
            return
        end
        local currentTime = System.currentTimeMillis()
        if currentTime - lastFaceTime > 2000 then
            if currentTime - lastSpeakTime > 2000 then
                service.speak(guidance.noFace)
                lastSpeakTime = currentTime
            end
        end
        task(1000, checkLoop)
    end
    checkLoop()
end

faceListener = luajava.createProxy("android.hardware.Camera$FaceDetectionListener", {
    onFaceDetection = function(faces, cam)
        if isCapturing or isCountingDown or dialogPauseCount > 0 then return end
        local currentTime = System.currentTimeMillis()
        local success, face = pcall(function() return faces[0] end)
        if success and face then
            lastFaceTime = currentTime
            if currentTime - lastSpeakTime > 2000 then 
                local rawX = face.rect.centerX()
                local rawY = face.rect.centerY()
                local width = face.rect.width()
                local diffX = 0
                local diffY = 0
                if camOrientation == 90 then
                    diffX = rawY
                    diffY = rawX
                elseif camOrientation == 270 then
                    diffX = -rawY
                    diffY = -rawX
                else
                    diffX = rawX
                    diffY = rawY
                end
                local errorX = math.abs(diffX)
                local errorY = math.abs(diffY)
                local instruction = ""
                local minWidth = 100  
                local maxWidth = 900  
                local centerTolerance = 260 
                if errorX > centerTolerance or errorY > centerTolerance then
                    if errorX > errorY then
                        if diffX > 0 then instruction = guidance.right else instruction = guidance.left end
                    else
                        if diffY > 0 then instruction = guidance.down else instruction = guidance.up end
                    end
                elseif width < minWidth then 
                    instruction = guidance.near
                elseif width > maxWidth then 
                    instruction = guidance.far
                else
                    if timerValue > 0 then
                        isCountingDown = true
                        instruction = timerValue .. (currentLang == "Arabic" and " ثانية مؤقت بدأ..." or "s timer started...")
                        service.speak(instruction)
                        task(timerValue * 1000, function() isCountingDown = false; executeCapture() end)
                        lastSpeakTime = currentTime + (timerValue * 1000) + 2000
                        return
                    else
                        instruction = guidance.perfect
                        service.speak(instruction)
                        lastSpeakTime = currentTime + 4000 
                        task(1200, function() executeCapture() end)
                        return
                    end
                end
                if instruction ~= "" then
                    service.speak(instruction)
                    lastSpeakTime = currentTime
                end
            end
        end
    end
})

function openCameraInstance()
    if camera then 
        pcall(function() camera.stopFaceDetection(); camera.stopPreview(); camera.release() end) 
    end
    pcall(function() 
        local CameraInfo = luajava.bindClass("android.hardware.Camera$CameraInfo")
        local info = CameraInfo()
        Camera.getCameraInfo(cameraId, info)
        camOrientation = info.orientation 
    end)
    local success, err = pcall(function()
        camera = Camera.open(cameraId)
        camera.setPreviewDisplay(surface_view.getHolder())
        local params = camera.getParameters()
        if cameraId == 0 then
            local focusModes = params.getSupportedFocusModes()
            if focusModes and focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE) then 
                params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE) 
            end
        end
        local sizes = params.getSupportedPictureSizes()
        if sizes then
            local bestSize = sizes.get(0)
            for i=0, sizes.size()-1 do
                local s = sizes.get(i)
                if s.width * s.height > bestSize.width * bestSize.height then
                    bestSize = s
                end
            end
            params.setPictureSize(bestSize.width, bestSize.height)
        end
        if qualityLevel == 1 then 
            params.setJpegQuality(100) 
        elseif qualityLevel == 2 then 
            params.setJpegQuality(70) 
        else 
            params.setJpegQuality(30) 
        end
        camera.setParameters(params)
        if params.getMaxNumDetectedFaces() > 0 then
            camera.startPreview()
            lastFaceTime = System.currentTimeMillis()
            lastSpeakTime = System.currentTimeMillis() + 1500 
            camera.setFaceDetectionListener(faceListener)
            camera.startFaceDetection()
            isCapturing = false
            isCountingDown = false
            startContinuousChecker()
        else
            service.speak("Face detection unsupported.")
        end
    end)
    if not success then service.speak("Camera error.") end
end

dlg.show()

pcall(function()
    local sm = service.getSystemService("sensor")
    local acc = sm.getDefaultSensor(1)
    local lastShakeTime = 0
    local sl = luajava.createProxy("android.hardware.SensorEventListener", {
        onSensorChanged = function(event)
            if not isRecording or not isShakeToStopOn then return end
            local x, y, z = event.values[0], event.values[1], event.values[2]
            local gForce = math.sqrt(x*x + y*y + z*z) / 9.80665
            if gForce > shakeIntensity then
                if (System.currentTimeMillis() - lastShakeTime) > 1500 then
                    lastShakeTime = System.currentTimeMillis()
                    mainHandler.post(luajava.createProxy("java.lang.Runnable", {run=function() stopVideoRecording() end}))
                end
            end
        end,
        onAccuracyChanged = function(s, a) end
    })
    sm.registerListener(sl, acc, 3)
end)

local decorView = dlg.getWindow().getDecorView()
decorView.setFocusable(true)
decorView.setFocusableInTouchMode(true)
decorView.requestFocus()

service.speak(t.title)

task(1200, function()
    openCameraInstance()
end)